import json
import random
import re
from django import http
from django.conf import settings
from django.contrib.auth import login, logout, authenticate
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views import View
from django_redis import get_redis_connection

from areas.models import Address, Area
from goods.models import SKU
from mail_token import encode_signature_mail_url, decode_signature_mail_url
from myitsdangerous import encode_signature_openid, decode_signature_openid
from response_code import RET, RETCODE
from users.models import User
from tasks.mail import send_mail_celery
from verifications import constants
from tasks.sms import send_sms


class RegisterView(View):
    def get(self, request):
        return render(request, 'register_vue.html', {'error_name_message': 'django的模板数据'})

    # 用户注册接口
    def post(self, request):
        # 1, 初始化数据
        data = request.POST
        user_name = data.get('user_name')
        pwd = data.get('pwd')
        cpwd = data.get('cpwd')
        phone = data.get('phone')
        msg_code = data.get('msg_code')
        allow = data.get('allow')

        # 1,1 判断数据是否都有
        if not all([user_name, pwd, cpwd, phone, msg_code, allow]):
            return http.HttpResponseForbidden('参数不全')

        # 1,2 校验用户名
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', user_name):
            return http.HttpResponseForbidden('用户名格式有误')

        # 1,3 校验密码
        if not re.match(r'^[0-9A-Za-z]{8,20}$', pwd):
            return http.HttpResponseForbidden('密码格式有误')

        # 1,4 两遍密码是否相同
        if pwd != cpwd:
            return http.HttpResponseForbidden('两遍密码不同')

        # 1,5 校验手机号
        if not re.match(r'^1[3-9]\d{9}$', phone):
            return http.HttpResponseForbidden('手机号格式不正确')
        # 1,6校验验证码
        redis_conn = get_redis_connection('verify_code')
        redis_msg_code = redis_conn.get('sms_%s' % phone).decode()
        if msg_code is None:
            return http.HttpResponseForbidden('短信验证码不存在')
        if msg_code != redis_msg_code:
            return http.HttpResponseForbidden('短信验证码错误')
        # # 1,7校验图形验证码
        # uuid = request.GET.get('image_code_id')
        # redis_conn = get_redis_connection('verify_code')
        # redis_image_code = redis_conn.get('img_%s'% uuid)


        if allow != 'on':
            return http.HttpResponseForbidden('请同意用户协议')

        # 2, 处理业务
        # 2,1 创建用户
        try:
            user = User.objects.create_user(username=user_name, password=pwd, mobile=phone)
        except Exception as e:
            # return render(request, 'register_vue.html', {'register_errmsg': '注册失败'})
            return http.HttpResponseForbidden('注册失败')
        login(request, user)
        res = redirect(reverse('index'))
        res.set_cookie('username', user.username)
        return res


class UsernameCountView(View):
    def get(self, request, username):
        # 1, 初始化数据
        pass

        # 2, 处理业务逻辑
        count = User.objects.filter(username=username).count()

        # 3, 返回响应
        # res = {
        #     'code': RET.OK,
        #     'errmsg': 'success',
        #     'count': count
        # }

        return http.JsonResponse({'code': RET.OK, 'errmsg': 'success', 'count': count})


# 手机号重复检测
class MobileCountView(View):
    def get(self, request, mobile):
        # 1,初始化数据
        pass

        # 2,处理业务逻辑
        count = User.objects.filter(mobile=mobile).count()

        # 3,返回响应
        # res = {
        #     'code': RET.OK,
        #     'errmsg': 'success',
        #     'count': count
        # }
        return http.JsonResponse({'code': RET.OK, 'errmsg': 'success', 'count': count})


class LoginView(View):
    def get(self, request):
        return render(request, 'login.html')

    def post(self, request):
        username = request.POST.get('username')
        password = request.POST.get('pwd')
        remembered = request.POST.get('remembered')

        if not all([username, password]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'[a-zA-Z0-9_-]{5,20}$', username):
            return http.HttpResponseForbidden('请输入正确的用户名或手机号')
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return http.HttpResponseForbidden('密码最少8位，最长20位')
        # #1账号登陆
        # try:
        #     user=User.objects.get(username=username)
        # except Exception as e:
        #     user=None
        # # # 手机号登陆
        # if not user:
        #     try:
        #         user = User.objects.get(mobile=username)
        #     except Exception as e:
        #         return http.HttpResponseForbidden('用户不存在')
        # #校验密码
        # if user.check_password(password)==False:
        #     return http.HttpResponseForbidden('密码不正确')
        # login(request, user)
        '''正常情况下通过django自带的用户创建函数create_user进行用户创建，
        可将用户密码进行加密，再用authenticate函数就可以对用户名和密码进行校验'''
        user = authenticate(username=username, password=password)
        if user is None:
            return http.HttpResponseForbidden('用户名或密码错误')

        login(request, user)
        res = redirect('index')
        res.set_cookie('username', user.username)
        return res


class LogoutView(View):
    def get(self, request):
        logout(request)
        res = redirect(reverse('index'))
        res.delete_cookie('username')
        return res


# # Django用户认证系统提供了装饰器login_required来判断用户是否登录。
# @method_decorator(login_required)
# dispacth是在所有方法上加装装饰器
# @method_decorator(login_required,name='dispatch')
class LoginRequired(View):
    @method_decorator(login_required)
    # 重新定义dispatch
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)


class UserCenterView(LoginRequired):
    def get(self, request):
        # #Django用户认证系统提供了方法request.user.is_authenticated()来判断用户是否登录
        # if  request.user.is_authenticated():
        #     pass
        # else:
        #     return redirect(reverse('login'))
        context = {
            'username': request.user.username,
            'mobile': request.user.mobile,
            'email': request.user.email,
            'email_active': request.user.email_active
        }
        return render(request, 'user_center_info.html', context=context)


class EmailView(LoginRequired):
    def put(self, request):
        data = json.loads(request.body.decode())
        email = data.get('email')
        if not re.match((r'[a-z0-9][\w\.\-]*@[a-z0-9\-]+([\.[a-z]{2,5}){1,2}$'), email):
            return http.JsonResponse({'code': RET.PARAMERR, 'errmsg': '邮箱格式错误'})
        user = request.user
        user.email = email
        user.save()
        token_str = encode_signature_mail_url({"user": user.username})
        verify_url = settings.EMAIL_VERIFY_URL + "?token=" + token_str
        send_mail_celery('验证邮件', verify_url, user.email)
        return http.JsonResponse({'code': RET.OK, 'errmsg': 'success'})


class EmailVerifyView(View):
    def get(self, request):
        token = request.GET.get('token')
        # 假设token解密失败，就没有data
        try:
            data = decode_signature_mail_url(token)
            # data={"user": username}
        except Exception as e:
            data = None
        if not data:
            return http.HttpResponseForbidden('token失效了')
        username = data.get('user')
        try:
            user = User.objects.get(username=username)
        except Exception as e:
            user = None
        if not user:
            return http.HttpResponseForbidden('无效用户')
        user.email_active = True
        user.save()
        return redirect(reverse('user_center'))


class AddressView(LoginRequired):
    def get(self, request):
        user = request.user
        addresses = user.addresses.filter(is_deleted=False)#排除逻辑删除的数据
        # print(addresses)
        address_list = []
        for address in addresses:
            address_data = {
                'id': address.id,  # 前端需要id来判断是否和默认地址id相同
                'title': address.title,
                'receiver': address.receiver,
                'province': address.province.name,  # <Area: 山西省>
                'province_id': address.province.id,  # <Area: 山西省>
                'city': address.city.name,
                'city_id': address.city.id,
                'district': address.district.name,
                'district_id': address.district.id,
                'place': address.place,
                'mobile': address.mobile,
                'tel': address.tel,
                'email': address.email,
            }
            address_list.append(address_data)
        context = {
            'addresses': json.dumps(address_list),
            'default_address_id': user.default_address_id
        }
        return render(request, 'user_center_site.html', context=context)


class CreateAddressView(LoginRequired):
    def post(self, request):
        data=json.loads(request.body.decode())
        city_id=data.get('city_id')
        district_id=data.get('district_id')
        email=data.get('email')
        mobile=data.get('mobile')
        place=data.get('place')
        province_id=data.get('province_id')
        receiver=data.get('receiver')
        tel=data.get('tel')
        title=data.get('title')
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')
        try:
            address=Address.objects.create(
                user=request.user,
                title=title,
                receiver=receiver,
                province_id=province_id,#外键
                city_id=city_id,#外键
                district_id=district_id,#外键
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
        except Exception as e:
            return http.JsonResponse({'code':'','errmsg':'地址创建失败'})

        address_data = {
            'id': address.id,  # 前端需要id来判断是否和默认地址id相同
            'title': address.title,
            'receiver': address.receiver,
            'province': address.province.name,  # <Area: 山西省>
            'province_id': address.province.id,  # <Area: 山西省>
            'city': address.city.name,
            'city_id': address.city.id,
            'district': address.district.name,
            'district_id': address.district.id,
            'place': address.place,
            'mobile': address.mobile,
            'tel': address.tel,
            'email': address.email,
        }
        return http.JsonResponse({'code':RET.OK,'errmsg':'地址创建成功','address':address_data})
class UpdateDestroyAddressView(LoginRequired):
    def put(self,request,address_id):
        data=json.loads(request.body.decode())
        receiver = data.get('receiver')
        province_id = data.get('province_id')
        city_id = data.get('city_id')
        district_id = data.get('district_id')
        place = data.get('place')
        mobile = data.get('mobile')
        tel = data.get('tel')
        email = data.get('email')
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')
        try:
            Address.objects.filter(id=address_id).update(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
        except Exception as e:
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '更新地址失败'})
        address=Address.objects.get(id=address_id)
        address_data = {
            'id': address.id,  # 前端需要id来判断是否和默认地址id相同
            'title': address.title,
            'receiver': address.receiver,
            'province': address.province.name,  # <Area: 山西省>
            'province_id': address.province.id,  # <Area: 山西省>
            'city': address.city.name,
            'city_id': address.city.id,
            'district': address.district.name,
            'district_id': address.district.id,
            'place': address.place,
            'mobile': address.mobile,
            'tel': address.tel,
            'email': address.email,
        }
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '更新地址成功', 'address':address_data})

    def delete(self,request,address_id):
        try:
            address=Address.objects.get(id=address_id)
        except Exception as e:
            return http.JsonResponse({'code': RET.PARAMERR, 'errmsg': '地址编号错误'})
        address.is_deleted=True
        address.save()
        return http.JsonResponse({'code': RET.OK, 'errmsg': '删除成功'})
class DefaultAddressView(LoginRequired):
    def put(self,request,address_id):
        try:
            address = Address.objects.get(id=address_id)
            request.user.default_address=address
            request.user.save()
        except Exception as e:
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置默认地址失败'})
        return http.JsonResponse({'code': RET.OK, 'errmsg': '设置默认地址成功'})
class UpdateTitleAddressVie(LoginRequired):
    def put(self,request,address_id):
        data=json.loads(request.body.decode())
        title=data.get('title')
        try:
            address=Address.objects.get(id=address_id)
            address.title=title
            address.save()
        except Exception as e:
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置地址标题失败'})
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '设置地址标题成功'})
class ChangePasswordView(LoginRequired):
    def get(self,request):
        return render(request,'user_center_pass.html')
    def post(self,request):
        old_pwd=request.POST.get('old_pwd')
        new_pwd=request.POST.get('new_pwd')
        new_cpwd=request.POST.get('new_cpwd')
        user=request.user
        if not all([old_pwd, new_pwd, new_cpwd]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not user.check_password(old_pwd):
            return http.HttpResponseForbidden('密码不正确')
        if not re.match(r'^[0-9A-Za-z]{8,20}$', new_pwd):
            return http.HttpResponseForbidden('密码格式不正确')
        if new_pwd !=new_cpwd:
            return http.HttpResponseForbidden('两遍密码不一致')
        if old_pwd == new_pwd:
            return http.HttpResponseForbidden('新密码和旧密码不能相同')
        user.set_password(new_pwd)
        user.save()
        logout(request)
        res=redirect(reverse('login'))
        res.delete_cookie('username')
        return res
class UserBrowseHistory(LoginRequired,View):
    def post(self,request):
        user = request.user
        data=json.loads(request.body.decode())#拿到请求体
        sku_id=data.get('sku_id')
        try:
            sku=SKU.objects.get(id=sku_id)
        except Exception as e:
            return http.JsonResponse({'code':RET.PARAMERR,'errmsg':'商品编号错误'})
        #创建游标对象
        redis_conn=get_redis_connection('history')
        # 先去重
        redis_conn.lrem('history_%s'%user.id,0,sku_id)
        # 再存储
        redis_conn.lpush('history_%s'%user.id,sku_id)
        # 最后截取
        redis_conn.ltrim('history_%s'%user.id,0,4)
        return http.JsonResponse({'code':RET.OK,'errmsg': 'OK'})
    def get(self,request):
        user=request.user
        redis_conn=get_redis_connection('history')
        sku_ids=redis_conn.lrange('history_%s'%user.id,0,-1)
        skus=[]
        for sku_id in sku_ids:
            sku=SKU.objects.get(id=sku_id)
            sku_data={
                'id':sku.id,
                'default_image_url':sku.default_image_url.url,
                'name':sku.name,
                'price':sku.price,
            }
            skus.append(sku_data)
        # print(skus)
        return http.JsonResponse({'code':RET.OK,'errmsg': 'OK','skus':skus})
#渲染忘记密码页面
class FindPwdView(View):
    def get(self,request):
        return render(request, 'find_password.html')
#第一步 校验用户名和图形验证码
class PwdImage(View):
    def get(self,request,username):
        text = request.GET.get('text')
        image_code_id = request.GET.get('image_code_id')
        #验证用户是否正确
        try:
            user=User.objects.get(username=username)
        except Exception as e:
            return http.JsonResponse({'error':'用户身份错误'},status=404)
        #验证参数是否齐全
        if not all([text,image_code_id]):
            return http.JsonResponse({'error':'缺少必传参数'},status=403)
        #从redis中读取之前保存的图形验证码文本
        redis_conn=get_redis_connection('verify_code')
        redis_image_code=redis_conn.get('img_%s'%image_code_id)
        #如果redis中的数据过期则提示
        if redis_image_code is None:
            return http.JsonResponse({'error':'验证码失效'},status=403)
        #转小写后比较验证码
        if text.lower() != redis_image_code.decode().lower():
            return http.JsonResponse({'error':'验证码错误'},status=400)
        #返回结果：json数据
        userinfo=({'user_id': user.id, 'mobile': user.mobile})
        access_token = encode_signature_openid(userinfo)
        return http.JsonResponse({'mobile': user.mobile, 'access_token': access_token})
#第二步 01发送短信
class PwdSendSMS(View):
    def get(self, request):
        access_token = decode_signature_openid(request.GET.get('access_token'))
        if access_token is None:
            return http.JsonResponse({'error':'缺少必传参数'},status=403)
        mobile=access_token['mobile']
        try:
            User.objects.get(mobile=mobile)
        except Exception as e:
            return http.JsonResponse({'error':'用户身份错误'},status=404)
        redis_conn = get_redis_connection('verify_code')
        # 判断是否发送中
        redis_sms_flag = redis_conn.get('sms_flag_%s' % mobile)
        if redis_sms_flag:
            return http.JsonResponse({'error':'不能频繁请求短信'},status=403)
        #生成随机4位数，写入redis
        sms_code='%04d'%random.randint(0,9999)
        ## 创建Redis管道
        pl=redis_conn.pipeline()
        # 保存短信验证码，短信验证码过期时间
        pl.setex('sms_%s' % mobile, constants.SMS_CODE_EXPIRY, sms_code)
        # 重新写入send_flag，短信验证码频繁请求限制时间
        pl.setex('sms_flag_%s'% mobile,constants.SMS_CODE_REQ_LIMIT_TIME,'sending')
        pl.execute()
        #发送短信
        send_sms.delay(mobile, sms_code, 5, 1)
        return http.JsonResponse({'message': 'success'})
#第二步 02校验短信验证码
class PwdCheckSMS(View):
    def get(self,request,username):
        sms_code=request.GET.get('sms_code')
        user=User.objects.get(username=username)
        try:
            user=User.objects.get(username=username)
        except Exception as e:
            return http.JsonResponse({'error':'用户身份错误'},status=404)
        #从redis获取数据
        redis_conn = get_redis_connection('verify_code')
        redis_sms_code=redis_conn.get('sms_%s'% user.mobile)
        if redis_sms_code is None:
            return http.JsonResponse({'code':RET.SMSCODERR,'errmsg':'短信验证码已经过期'})
        #判断是否正确
        if sms_code != redis_sms_code.decode():
            return http.JsonResponse({'error':'短信验证码错误'},status=400)
        userinfo=({'user_id': user.id, 'mobile': user.mobile})
        access_token=encode_signature_openid(userinfo)
        return http.JsonResponse({'user_id':user.id,'access_token':access_token})
#第三步 更改密码
class ChangePwdView(View):
    def post(self,request,user_id):
        data=json.loads(request.body.decode())
        password=data.get('password')
        password2=data.get('password2')
        access_token=decode_signature_openid(data.get('access_token'))
        if not all([password,password2,access_token]):
            return http.JsonResponse({'error':'缺少必传参数'},status=403)
        if password != password2:
            return http.JsonResponse({'error':'两次密码不一致'},status=403)
        if int(user_id) != access_token['user_id']:
            return http.JsonResponse({'error':'用户身份错误'},status=404)
        try:
            user=User.objects.get(id=user_id)
        except Exception as e:
            return http.JsonResponse({'error':'用户身份错误'}, status = 404)
        user.set_password(password)
        user.save()
        response = http.JsonResponse({'message': 'success'})
        # 清理状态保持信息
        response.delete_cookie('username')
        # # 响应密码修改结果
        return response
